package org.dei.perla.rockfall.gui;

import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

import javax.swing.JComboBox;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextPane;

import org.dei.perla.parser.expressions.ConstantBlob;
import org.dei.perla.parser.expressions.ConstantCastException;
import org.dei.perla.sys.device.fpc.dspic.DSPicFPC;
import org.dei.perla.sys.llqruntime.datastructures.Record;
import org.dei.perla.utils.pipe.Pipe;
import org.dei.perla.utils.waiter.Waitable;
import org.dei.perla.utils.waiter.Waiter;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;

/**
 * Classe DataViewer: visualizza i Record provenienti da un DSPicFPC
 */
public class IntecDataViewer extends AbstractDataViewer {
	
	private static final long serialVersionUID = 2190271589396068772L;
	
	static int frameCount = 0;
	static int xPos = 30;
	static int yPos = 30;
	
	private XYSeriesCollection dataset;
	
	private final Waiter<Pipe<Record>> pipeWaiter;
	private final Thread pipeWaiterThread;
	private Record record;
	
	private final JTextPane samplingDataTextArea;
	private final JScrollPane samplingScrollPane;
	
	private final ArrayList<IntecSingleEvent> eventsData;
	
	private final JComboBox dataSelectorComboBox;

	/**
	 * Costruttore
	 * @param title Titolo dell'InnerFrame
	 * @param recordPipe Pipe dalla quale ricevere i Record
	 */
	public IntecDataViewer(String title, Pipe<Record> recordPipe) {
		
		// Impostazioni JInnerFrame
		super(title);
		setMaximizable(true);
		setIconifiable(true);
		setResizable(true);
		setClosable(false);
		setBounds(xPos, yPos, 500, 500);
		xPos += 30;
		yPos = xPos;
		getContentPane().setLayout(new GridBagLayout());
		GridBagConstraints gbc = new GridBagConstraints();
		
		// ArrayList per memorizzare i dati ricevuti dalle pipe
		eventsData = new ArrayList<IntecSingleEvent>();
		
		// Imposto un grafico preliminare da visualizzare mentre non ho ancora ricevuto alcun dato
	    // Aggiungo i pannelli all'interfaccia
        JTabbedPane tabbedPane = new JTabbedPane();
        
		// Inizializzazione area di testo per i dati relativi al sampling
		samplingDataTextArea = new JTextPane();
		samplingDataTextArea.setEditable(false);
		samplingScrollPane = new JScrollPane(samplingDataTextArea);
		samplingDataTextArea.setText("Nessun dato da visualizzare");
        tabbedPane.addTab("Dati campionamento", samplingScrollPane);
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 0.5;
        gbc.weighty = 0.5;
        gbc.anchor = GridBagConstraints.NORTH;
        gbc.fill = GridBagConstraints.BOTH;
        this.getContentPane().add(tabbedPane, gbc);
        
        // Inizializzazione selettore dei dati da visualizzare
        dataSelectorComboBox = new JComboBox();
        dataSelectorComboBox.addActionListener(this);
        JPanel panel = new JPanel(new GridBagLayout());
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        panel.add(new JLabel("Eventi disponibili: "));
        gbc.gridx = 1;
        gbc.gridy = 0;
        panel.add(dataSelectorComboBox, gbc);
        
        gbc.gridx = 0;
        gbc.gridy = 1;
        getContentPane().add(panel, gbc);
        
        // Thread di attesa per i dati sulla pipe
        pipeWaiter = new Waiter<Pipe<Record>>();
        pipeWaiter.start();
        pipeWaiter.addWaitable(recordPipe.getWaitable());
        pipeWaiterThread = new Thread(new Runnable() {
        	public void run() {
        		Waitable<Pipe<Record>> waitable;
        		
        		do {
        			waitable = pipeWaiter.waitNext();
        			record = waitable.getParentComponent().dequeue();
        			if (record == null) {
        				waitable.getParentComponent().stop();
        				pipeWaiter.stop();
        				setTitle("DSPic - Terminated");
        				break;
        			}
        			
        			// Eseguo l'aggiornamento del grafico
        			try {
						javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
							public void run() {
								setData(record);
							}
						});
					} catch (InterruptedException e) {
						System.out.println("Thread swing di aggiornamento dati interfaccia grafica interrotto: " + e.getMessage());
					} catch (InvocationTargetException e) {
						System.out.println("Thread swing di aggiornamento dati interfaccia grafica fallito: " + e.getMessage());
					}
        		} while(true);
        	}
        });
        pipeWaiterThread.start();
	}
	
	/**
	 * Visualizza il record nell'interfaccia grafica
	 * @param record record da visualizzare
	 */
	public void setData(Record record) {

		StringBuffer text = new StringBuffer();
	
		try {
			text.append("Low Sampling Rate Data\n");
			text.append("Voltage of the panel: " + record.getField("V_PANNELLO").getValueInt() + "\n");
			text.append("Current from the panel: " + record.getField("C_PANNELLO").getValueInt() + "\n");
			text.append("Voltage of the battery 1: " + record.getField("V_BATTERIA_1").getValueInt() + "\n");
		} catch (ConstantCastException e) {
			text.append("Errore nel recupero dei dati dal record: " + e.getMessage());
			System.out.println("Errore nel recupero dei dati dal record: " + e.getMessage());
		}
		samplingDataTextArea.setText(text.toString());
		storeEventData(new IntecSingleEvent(text.toString()));
	}
	
	/**
	 * Memorizza i dati relativi ad un singolo evento ed aggiorna il selettore
	 * @param singleEvent
	 */
	private void storeEventData(IntecSingleEvent singleEvent) {
		eventsData.add(singleEvent);
		dataSelectorComboBox.addItem(eventsData.size());
		dataSelectorComboBox.setSelectedIndex(eventsData.size() - 1);
	}
	
	/**
	 * Converte una coppia di byte in un intero
	 * @param b0 byte meno significativo
	 * @param b1 byte piu' significativo
	 * @return numero intero creato dall'unione dei due byte
	 */
	private int convertToInt(byte b0, byte b1) {
		Integer readData;
		
		if(b1 >= 0) {
			readData = (((int)b1 << 8) & 0x0000ff00) | ((int)b0&0x000000ff);
		} else {
			readData = 0xffff0000 | (((int)b1 << 8) & 0x0000ff00) | ((int)b0&0x000000ff) ;
		}
		
		return readData;
	}
	
	/**
	 * Metodo di implementazione dell'ActionListener per il selettore dei dati da visualizzare
	 */
	public void actionPerformed(ActionEvent e) {
		if (e.getSource() instanceof JComboBox) {
			IntecSingleEvent event = eventsData.get(((JComboBox)e.getSource()).getSelectedIndex());
			samplingDataTextArea.setText(event.getSampleData());
		}
	}
	
	/**
	 * InnerClass per la memorizzazione dei dati relativi ad un singolo evento
	 */
	private final class IntecSingleEvent {
		
		private final String sampleData;
		
		public IntecSingleEvent(String sampleData) {
			this.sampleData = sampleData;
		}
		
		public String getSampleData() {
			return sampleData;
		}		
	}
}
